package com.github.florent37.mylittlecanvas.animation;

import com.github.florent37.mylittlecanvas.shape.RectShape;
import ohos.agp.animation.AnimatorValue;

public class RectShapeAnimation<S extends RectShape> extends ShapeAnimation<S> {

    public RectShapeAnimation(S shape) {
        super(shape);
    }

    //region left

    /**
     * Change the left of the rect
     * Warning /!\ it change the width of the view
     * Use .moveLeft to keep the view width
     *
     * @param values the values
     * @return AnimatorValue
     */
    public AnimatorValue left(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.setLeft(AnimatorValueUtils.getAnimatedValue(v, values));
            }
        });
        return animatorValue;
    }

    public AnimatorValue leftTo(final float... values) {
        return left(insertAtFirst(shape.getLeft(), values));
    }

    public AnimatorValue leftBy(float... values) {
        final float[] newValues = new float[values.length];
        final float left = shape.getLeft();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] * left;
        }
        return left(newValues);
    }

    public AnimatorValue leftPlus(float... values) {
        final float[] newValues = new float[values.length];
        final float left = shape.getLeft();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] + left;
        }
        return leftTo(newValues);
    }

    /**
     * Move the rect to the left, keeping his width
     *
     * @param values the values
     * @return AnimatorValue
     */
    public AnimatorValue moveLeftTo(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.moveLeftTo(AnimatorValueUtils.getAnimatedValue(v, insertAtFirst(shape.getLeft(), values)));
            }
        });
        return animatorValue;
    }
    //endregion

    //region right

    /**
     * Change the right of the rect
     * Warning /!\ it change the width of the view
     * Use .moveRight to keep the view width
     *
     * @param values the values
     * @return AnimatorValue
     */
    public AnimatorValue right(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.setRight(AnimatorValueUtils.getAnimatedValue(v, values));
            }
        });
        return animatorValue;
    }

    public AnimatorValue rightTo(final float... values) {
        return right(insertAtFirst(shape.getRight(), values));
    }

    public AnimatorValue rightPlus(float... values) {
        final float[] newValues = new float[values.length];
        final float right = shape.getRight();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] + right;
        }
        return rightTo(newValues);
    }

    public AnimatorValue rightBy(float... values) {
        final float[] newValues = new float[values.length];
        final float right = shape.getRight();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] * right;
        }
        return right(newValues);
    }

    /**
     * Move the rect to the right, keeping his height
     *
     * @param values the values
     * @return AnimatorValue
     */
    public AnimatorValue moveRightTo(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.moveRightTo(AnimatorValueUtils.getAnimatedValue(v, insertAtFirst(shape.getRight(), values)));
            }
        });
        return animatorValue;
    }

    //endregion

    //region top

    /**
     * Change the top of the rect
     * Warning /!\ it change the height of the view
     * Use .moveTop to keep the view height
     *
     * @param values the values
     * @return AnimatorValue
     */
    public AnimatorValue top(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.setTop(AnimatorValueUtils.getAnimatedValue(v, values));
            }
        });
        return animatorValue;
    }

    public AnimatorValue topTo(final float... values) {
        return top(insertAtFirst(shape.getTop(), values));
    }

    public AnimatorValue topBy(float... values) {
        final float[] newValues = new float[values.length];
        final float top = shape.getTop();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] * top;
        }
        return top(newValues);
    }

    public AnimatorValue topPlus(float... values) {
        final float[] newValues = new float[values.length];
        final float top = shape.getTop();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] + top;
        }
        return topTo(newValues);
    }

    /**
     * Move the rect to the top, keeping his height
     *
     * @param values the values
     * @return AnimatorValue
     */
    public AnimatorValue moveTopTo(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.moveTopTo(AnimatorValueUtils.getAnimatedValue(v, insertAtFirst(shape.getTop(), values)));
            }
        });
        return animatorValue;
    }

    //endregion

    //region bottom

    /**
     * Change the bottom of the rect
     * Warning /!\ it change the height of the view
     * Use .moveBottom to keep the view height
     *
     * @param values the values
     * @return AnimatorValue
     */
    public AnimatorValue bottom(float... values) {
        final float[] newValues = new float[values.length + 1];
        newValues[0] = shape.getBottom();
        for (int i = 0; i < values.length; i++) {
            newValues[i + 1] = values[i];
        }

//        final ValueAnimator valueAnimator = ValueAnimator.ofFloat(newValues);
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.setBottom(AnimatorValueUtils.getAnimatedValue(v, values));
            }
        });
        return animatorValue;
    }

    public AnimatorValue bottomTo(final float... values) {
        return bottom(insertAtFirst(shape.getBottom(), values));
    }

    public AnimatorValue bottomBy(float... values) {
        final float[] newValues = new float[values.length];
        final float bottom = shape.getBottom();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] * bottom;
        }
        return bottom(newValues);
    }

    public AnimatorValue bottomPlus(float... values) {
        final float[] newValues = new float[values.length];
        final float bottom = shape.getBottom();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] + bottom;
        }
        return bottomTo(newValues);
    }

    /**
     * Move the rect to the bottom, keeping his height
     *
     * @param values the values
     * @return AnimatorValue
     */
    public AnimatorValue moveBottomTo(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.moveBottomTo(AnimatorValueUtils.getAnimatedValue(v, insertAtFirst(shape.getBottom(), values)));
            }
        });
        return animatorValue;
    }

    //endregion

    public AnimatorValue borderColorTo(final int... color) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.setBorderColor(AnimatorValueUtils.getAnimatedColor(v, insertAtFirst(shape.getBorderColor(), color)));
            }
        });
        return animatorValue;
    }

    public AnimatorValue borderWidthTo(final float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.setBorderWidth(AnimatorValueUtils.getAnimatedValue(v, insertAtFirst(shape.getBorderWidth(), values)));
            }
        });
        return animatorValue;
    }

    public AnimatorValue centerX(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.moveCenterXTo(AnimatorValueUtils.getAnimatedValue(v, values));
            }
        });
        return animatorValue;
    }

    public AnimatorValue centerXTo(float... values) {
        return centerX(insertAtFirst(shape.getCenterX(), values));
    }

    public AnimatorValue centerXPlus(float... values) {
        final float[] newValues = new float[values.length];
        final float centerX = shape.getCenterX();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] + centerX;
        }
        return centerXTo(newValues);
    }

    public AnimatorValue centerY(float... values) {
        final AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                shape.moveCenterYTo(AnimatorValueUtils.getAnimatedValue(v, values));
            }
        });
        return animatorValue;
    }

    public AnimatorValue centerYTo(float... values) {
        return centerY(insertAtFirst(shape.getCenterY(), values));
    }

    public AnimatorValue centerYPlus(float... values) {
        final float[] newValues = new float[values.length];
        final float centerY = shape.getCenterY();
        for (int i = 0; i < values.length; i++) {
            newValues[i] = values[i] + centerY;
        }
        return centerYTo(newValues);
    }
}
